package com.baturu.vin.benz.dto;

/**
 * 从transbase数据库获取奔驰图片
 * @author jiangxinlei
 * @Time 2017/3/2.
 */

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.awt.image.WritableRaster;

public class ImageExtractorFV4 {

    static int[] codeBits = new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 5 };
    static int[] rightShift = new int[] { 30, 30, 30, 30, 30, 30, 30, 30, 28, 28, 28, 28, 26, 26, 24, 22 };
    static int[] codeBase = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 20, 20, 84, 340 };
    private int currentRow;
    protected int rows;
    protected int columns;
    protected int bitsPerPixel;
    protected BufferedImage image;
    protected byte[] colorArray;
    protected byte[] annotationData;
    protected IndexColorModel cm;

    public ImageExtractorFV4() {
        this.currentRow = 0;
        this.image = new BufferedImage(1024, 1280, 13);
        this.annotationData = null;
    }

    public BufferedImage getImage(byte[] fv4bytes, EPCImageViewerCalloutArray calloutArray) {
        if (fv4bytes == null) {
            return null;
        }
        try {
            int bufferOffset = 0;
            WritableRaster writableRaster = this.image.getRaster();
            DataBufferByte dataBuffer = (DataBufferByte)writableRaster.getDataBuffer();
            byte[] imageBuf = dataBuffer.getData();
            int count = this.decodeImageHeader(fv4bytes, 0, fv4bytes.length, calloutArray);
            if (count <= 0) {
                return null;
            }
            for (bufferOffset += count; bufferOffset < fv4bytes.length; bufferOffset += count) {
                count = this.decodeImageData(fv4bytes, bufferOffset, fv4bytes.length - bufferOffset, imageBuf);
                if (count < 0) {
                    return null;
                }
                if (count == 0) {
                    break;
                }
            }
        }
        catch (Throwable t) {
            t.printStackTrace();
        }
        return this.image;
    }

    public int decodeImageHeader(byte[] buffer, int bufferOffset, int bufferBytes, EPCImageViewerCalloutArray calloutArray) {
        int rasterOffset = 0;
        int rasterLength = 0;
        int annotationOffset = 0;
        int annotationLength = 0;
        this.bitsPerPixel = buffer[35];
        this.columns = ((buffer[38] & 0xFF) << 8) + (buffer[39] & 0xFF);
        this.rows = ((buffer[40] & 0xFF) << 8) + (buffer[41] & 0xFF);
        int overlays = buffer[45];
        if ((this.bitsPerPixel != 1 && this.bitsPerPixel != 2 && this.bitsPerPixel != 4) || this.columns != 1024 || this.rows > 1280 || overlays > 2) {
            return -1;
        }
        int offset = 46;
        while (overlays-- > 0) {
            if (buffer[offset] == 1 && buffer[offset + 1] == 1) {
                rasterOffset = ((buffer[offset + 6] & 0xFF) << 8) + (buffer[offset + 7] & 0xFF);
                rasterLength = ((buffer[offset + 10] & 0xFF) << 8) + (buffer[offset + 11] & 0xFF);
            }
            else if (buffer[offset] == 32 && buffer[offset + 1] == 1) {
                annotationOffset = ((buffer[offset + 6] & 0xFF) << 8) + (buffer[offset + 7] & 0xFF);
                annotationLength = ((buffer[offset + 10] & 0xFF) << 8) + (buffer[offset + 11] & 0xFF);
                System.arraycopy(buffer, annotationOffset, this.annotationData = new byte[annotationLength], 0, annotationLength);
                this.saveCallouts(this.annotationData, annotationLength, calloutArray);
            }
            offset += 16;
        }
        if (rasterOffset == 0 || rasterOffset + rasterLength > bufferOffset + bufferBytes || buffer[rasterOffset] != 1 || buffer[rasterOffset + 1] != 4) {
            return -1;
        }
        offset = rasterOffset + 18;
        int index = 0;
        int count = 1 << this.bitsPerPixel;
        this.colorArray = new byte[count];
        while (index < count) {
            int tmp;
            if ((index & 0x1) == 0x1) {
                tmp = (0xF & buffer[offset++]);
            }
            else {
                tmp = (0xF0 & buffer[offset]) >> 4;
            }
            if (this.bitsPerPixel == 1) {
                tmp <<= 3;
            }
            else if (this.bitsPerPixel == 2) {
                tmp <<= 2;
            }
            else {
                tmp <<= 1;
            }
            this.colorArray[index++] = buffer[2 + tmp];
        }
        this.cm = new IndexColorModel(this.bitsPerPixel, count, this.colorArray, this.colorArray, this.colorArray);
        int tmp;
        if (rasterOffset > annotationOffset) {
            tmp = rasterOffset + rasterLength - 1;
        }
        else {
            tmp = annotationOffset + annotationLength - 1;
        }
        tmp = (tmp + 1023) / 1024 * 1024;
        return tmp;
    }

    public int decodeImageData(byte[] buffer, int bufferOffset, int bufferBytes, byte[] imageBuf) {
        short[][] planesOdd = new short[this.bitsPerPixel][1026];
        short[][] planesEven = new short[this.bitsPerPixel][1026];
        byte[] imagePaletteIn = new byte[1025];
        int subgroupBits = ((buffer[bufferOffset + 6] & 0xFF) << 8) + (buffer[bufferOffset + 5] & 0xFF);
        int subgroupBytes = subgroupBits + 7 >> 3;
        if (subgroupBytes + 16 > bufferBytes) {
            return 0;
        }
        int subgroupStartRow = ((buffer[bufferOffset + 1] & 0xFF) << 8) + (buffer[bufferOffset] & 0xFF);
        if (buffer[bufferOffset + 4] != this.bitsPerPixel || subgroupStartRow != this.currentRow) {
            return 0;
        }
        int subgroupRows = ((buffer[bufferOffset + 3] & 0xFF) << 8) + (buffer[bufferOffset + 2] & 0xFF);
        int bufferIndex = bufferOffset + 16;
        int value = (buffer[bufferIndex++] & 0xFF) << 24;
        value |= (buffer[bufferIndex++] & 0xFF) << 16;
        value |= (buffer[bufferIndex++] & 0xFF) << 8;
        value |= (buffer[bufferIndex++] & 0xFF);
        {
            int iTmp = imagePaletteIn.length, index = 0;
            while (index < iTmp) {
                imagePaletteIn[index++] = 0;
            }
        }
        int bitCount = 0;
        boolean odd = false;
        while (subgroupRows-- > 0) {
            odd = !odd;
            byte imageMask = (byte)(1 << this.bitsPerPixel - 1);
            int bitPlane = 0;
            while (bitPlane < this.bitsPerPixel) {
                short[] planeCurrent;
                short[] planePrevious;
                if (odd) {
                    planeCurrent = planesOdd[bitPlane];
                    planePrevious = planesEven[bitPlane];
                }
                else {
                    planeCurrent = planesEven[bitPlane];
                    planePrevious = planesOdd[bitPlane];
                }
                ++bitPlane;
                int toggleSave;
                int toggle = toggleSave = 0;
                int indexCurrent = 0;
                int indexPrevious = 0;
                int column = 0;
                while (column < 1024) {
                    if ((value & 0xE0000000) == 0xE0000000) {
                        int index = (value & 0x1E000000) >> 25;
                        int iTmp = ImageExtractorFV4.codeBits[index];
                        value <<= 3 + iTmp;
                        bitCount += 3 + iTmp;
                        int runLength = value >>> ImageExtractorFV4.rightShift[index];
                        runLength += ImageExtractorFV4.codeBase[index];
                        iTmp <<= 1;
                        value <<= iTmp;
                        bitCount += iTmp;
                        column += runLength;
                    }
                    else {
                        int relLength;
                        if ((value & Integer.MIN_VALUE) == 0x0) {
                            relLength = 0;
                            value <<= 1;
                            ++bitCount;
                        }
                        else if ((value & 0x40000000) == 0x0) {
                            if ((value & 0x20000000) == 0x0) {
                                relLength = 1;
                            }
                            else {
                                relLength = -1;
                            }
                            value <<= 3;
                            bitCount += 3;
                        }
                        else {
                            int sign = value & 0x10000000;
                            int index = (value & 0xF000000) >> 24;
                            int iTmp = ImageExtractorFV4.codeBits[index];
                            value <<= 4 + iTmp;
                            bitCount += 4 + iTmp;
                            relLength = value >>> ImageExtractorFV4.rightShift[index];
                            relLength += ImageExtractorFV4.codeBase[index] + 2;
                            if (sign != 0) {
                                relLength = -relLength;
                            }
                            iTmp <<= 1;
                            value <<= iTmp;
                            bitCount += iTmp;
                        }
                        if (toggle != toggleSave) {
                            if (indexPrevious == 0) {
                                ++indexPrevious;
                            }
                            else {
                                --indexPrevious;
                            }
                        }
                        int iTmp;
                        if (indexCurrent == 0) {
                            iTmp = 0;
                        }
                        else {
                            iTmp = planeCurrent[indexCurrent - 1];
                        }
                        while (planePrevious[indexPrevious] <= iTmp) {
                            indexPrevious += 2;
                        }
                        column = planePrevious[indexPrevious] + relLength;
                        toggleSave = toggle;
                    }
                    planeCurrent[indexCurrent++] = (short)column;
                    byte[] array = imagePaletteIn;
                    int n = column;
                    array[n] |= imageMask;
                    if (bitCount >= 8) {
                        int iTmp = 0;
                        while (bitCount >= 8) {
                            bitCount -= 8;
                            subgroupBits -= 8;
                            if (bufferIndex >= buffer.length) {
                                break;
                            }
                            iTmp = (iTmp << 8 | (buffer[bufferIndex++] & 0xFF));
                        }
                        value |= iTmp << bitCount;
                    }
                    toggle ^= 0x1;
                }
                if (column != 1024) {
                    return -1;
                }
                planeCurrent[indexCurrent++] = (short)column;
                planeCurrent[indexCurrent++] = (short)column;
                byte[] array2 = imagePaletteIn;
                int n2 = column;
                array2[n2] |= imageMask;
                imageMask >>= 1;
            }
            imageMask = (byte)((1 << this.bitsPerPixel) - 1);
            if (imagePaletteIn[0] != 0) {
                imageMask ^= imagePaletteIn[0];
            }
            imagePaletteIn[0] = 0;
            int offset = this.currentRow * 1024;
            int iTmp = 0;
            while (iTmp++ < 1024) {
                switch (imageMask) {
                    case 0: {
                        imageBuf[offset] = 0;
                        break;
                    }
                    case 1: {
                        imageBuf[offset] = -27;
                        break;
                    }
                    case 2: {
                        imageBuf[offset] = -14;
                        break;
                    }
                    case 3: {
                        imageBuf[offset] = -1;
                        break;
                    }
                    default:break;
                }
                if (imagePaletteIn[iTmp] != 0) {
                    imageMask ^= imagePaletteIn[iTmp];
                    imagePaletteIn[iTmp] = 0;
                }
                ++offset;
            }
            ++this.currentRow;
        }
        subgroupBytes = (subgroupBytes + 1023) / 1024 * 1024;
        return subgroupBytes;
    }

    public void saveCallouts(byte[] annotationData, int annotationLength, EPCImageViewerCalloutArray calloutArray) {
        int len1 = annotationData[18] & 0xFF;
        int len2 = annotationData[19] & 0xFF;
        int len3 = 256 * len1 + len2;
        int count1 = annotationData[20] & 0xFF;
        int count2 = annotationData[21] & 0xFF;
        int count3 = 256 * count1 + count2;
        calloutArray.allocate(count3);
        for (int n = 0, i = 22; i < annotationLength && n < count3; ++i, ++n) {
            int si = i - 1;
            int x = 0;
            int x2 = annotationData[i++];
            if (x2 < 0) {
                x2 += 256;
            }
            x2 <<= 8;
            x |= x2;
            int x3 = annotationData[i++];
            if (x3 < 0) {
                x3 += 256;
            }
            x |= x3;
            int y = 0;
            int y2 = annotationData[i++];
            if (y2 < 0) {
                y2 += 256;
            }
            y2 <<= 8;
            y |= y2;
            int y3 = annotationData[i++];
            if (y3 < 0) {
                y3 += 256;
            }
            y |= y3;
            int counter;
            for (counter = 0; annotationData[i + counter] != 0; ++counter) {}
            String c = new String(annotationData, i, counter);
            calloutArray.addToX(x, n);
            calloutArray.addToY(y, n);
            calloutArray.addToCalloutDesc(c, n);
            for (i += counter; i < annotationData.length && (i - si) % len3 != 0; ++i) {}
            while (i < annotationData.length && (i - si) % 4 != 0) {
                ++i;
            }
        }
    }
}